home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-19 / gpt32src.zip / READLINE.C < prev    next >
C/C++ Source or Header  |  1992-03-25  |  13KB  |  567 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: readline.c,v 3.26 92/03/24 22:34:35 woo Exp Locker: woo $";
  3. #endif
  4.  
  5. /* GNUPLOT - readline.c */
  6. /*
  7.  * Copyright (C) 1986, 1987, 1990, 1991, 1992   Thomas Williams, Colin Kelley
  8.  *
  9.  * Permission to use, copy, and distribute this software and its
  10.  * documentation for any purpose with or without fee is hereby granted, 
  11.  * provided that the above copyright notice appear in all copies and 
  12.  * that both that copyright notice and this permission notice appear 
  13.  * in supporting documentation.
  14.  *
  15.  * Permission to modify the software is granted, but not the right to
  16.  * distribute the modified code.  Modifications are to be distributed 
  17.  * as patches to released version.
  18.  *  
  19.  * This software is provided "as is" without express or implied warranty.
  20.  * 
  21.  *
  22.  * AUTHORS
  23.  *
  24.  *   Original Software:
  25.  *     Tom Tkacik
  26.  *
  27.  *   Msdos port and some enhancements:
  28.  *     Gershon Elber and many others.
  29.  * 
  30.  * Send your comments or suggestions to 
  31.  *  info-gnuplot@ames.arc.nasa.gov.
  32.  * This is a mailing list; to join it send a note to 
  33.  *  info-gnuplot-request@ames.arc.nasa.gov.  
  34.  * Send bug reports to
  35.  *  bug-gnuplot@ames.arc.nasa.gov.
  36.  */
  37.  
  38. #ifdef READLINE
  39.  
  40. /* a small portable version of GNU's readline */
  41. /* this is not the BASH or GNU EMACS version of READLINE due to Copyleft 
  42.     restrictions */
  43. /* do not need any terminal capabilities except backspace,
  44. /* and space overwrites a character */
  45.  
  46. /* NANO-EMACS line editing facility */
  47. /* printable characters print as themselves (insert not overwrite) */
  48. /* ^A moves to the beginning of the line */
  49. /* ^B moves back a single character */
  50. /* ^E moves to the end of the line */
  51. /* ^F moves forward a single character */
  52. /* ^K kills from current position to the end of line */
  53. /* ^P moves back through history */
  54. /* ^N moves forward through history */
  55. /* ^H and DEL delete the previous character */
  56. /* ^D deletes the current character, or EOF if line is empty */
  57. /* ^L/^R redraw line in case it gets trashed */
  58. /* ^U kills the entire line */
  59. /* ^W kills last word */
  60. /* LF and CR return the entire line regardless of the cursor postition */
  61. /* EOF with an empty line returns (char *)NULL */
  62.  
  63. /* all other characters are ignored */
  64.  
  65. #include <stdio.h>
  66. #include <ctype.h>
  67. #include <signal.h>
  68.  
  69. /* SIGTSTP defines job control */
  70. /* if there is job control then we need termios.h instead of termio.h */
  71. #ifdef SIGTSTP
  72. #define TERMIOS
  73. #endif
  74.  
  75.  
  76. #ifndef MSDOS
  77.  
  78. /* UNIX specific stuff */
  79. #ifdef TERMIOS
  80. #include <termios.h>
  81. static struct termios orig_termio, rl_termio;
  82. #else
  83. #include <termio.h>
  84. static struct termio orig_termio, rl_termio;
  85. #endif /* TERMIOS */
  86. static int term_set = 0;    /* =1 if rl_termio set */
  87.  
  88. #else
  89.  
  90. /* MSDOS specific stuff */
  91. #define getc(stdin) msdos_getch()
  92. static char msdos_getch();
  93.  
  94. #endif /* MSDOS */
  95.  
  96.  
  97. /* is it <string.h> or <strings.h>?   just declare what we need */
  98. extern int strlen();
  99. extern char *strcpy();
  100. extern char *malloc();
  101.  
  102. #define MAXBUF    1024
  103. #define BACKSPACE 0x08    /* ^H */
  104. #define SPACE    ' '
  105.  
  106. struct hist {
  107.     char *line;
  108.     struct hist *prev;
  109.     struct hist *next;
  110. };
  111.  
  112. static struct hist *history = NULL;  /* no history yet */
  113. static struct hist *cur_entry = NULL;
  114.  
  115. static char cur_line[MAXBUF];  /* current contents of the line */
  116. static int cur_pos = 0;    /* current position of the cursor */
  117. static int max_pos = 0;    /* maximum character position */
  118.  
  119.  
  120. void add_history();
  121. static void fix_line();
  122. static void redraw_line();
  123. static void clear_line();
  124. static void clear_eoline();
  125. static void copy_line();
  126. static void set_termio();
  127. static void reset_termio();
  128.  
  129. char *
  130. readline(prompt)
  131. char *prompt;
  132. {
  133.  
  134.     char cur_char;
  135.     char *new_line;
  136.  
  137.     /* set the termio so we can do our own input processing */
  138.     set_termio();
  139.  
  140.     /* print the prompt */
  141.     fputs(prompt, stderr);
  142.     cur_line[0] = '\0';
  143.     cur_pos = 0;
  144.     max_pos = 0;
  145.     cur_entry = NULL;
  146.  
  147.     /* get characters */
  148.     for(;;) {
  149.         cur_char = getc(stdin);
  150.         if(isprint(cur_char)) {
  151.             int i;
  152.             for(i=max_pos; i>cur_pos; i--) {
  153.                 cur_line[i] = cur_line[i-1];
  154.             }
  155.             putc(cur_char, stderr);
  156.             cur_line[cur_pos] = cur_char;
  157.             cur_pos += 1;
  158.             max_pos += 1;
  159.             if (cur_pos < max_pos)
  160.                 fix_line();
  161.             cur_line[max_pos] = '\0';
  162.  
  163.         /* else interpret unix terminal driver characters */
  164. #ifdef VERASE
  165.         } else if(cur_char == orig_termio.c_cc[VERASE] ){  /* DEL? */
  166.             if(cur_pos > 0) {
  167.                 int i;
  168.                 cur_pos -= 1;
  169.                 putc(BACKSPACE, stderr);
  170.                 for(i=cur_pos; i<max_pos; i++)
  171.                     cur_line[i] = cur_line[i+1];
  172.                 max_pos -= 1;
  173.                 fix_line();
  174.             }
  175. #endif /* VERASE */
  176. #ifdef VEOF
  177.         } else if(cur_char == orig_termio.c_cc[VEOF] ){   /* ^D? */
  178.             if(max_pos == 0) {
  179.                 reset_termio();
  180.                 return((char *)NULL);
  181.             }
  182.             if((cur_pos < max_pos)&&(cur_char == 004)) { /* ^D */
  183.                 int i;
  184.                 for(i=cur_pos; i<max_pos; i++)
  185.                     cur_line[i] = cur_line[i+1];
  186.                 max_pos -= 1;
  187.                 fix_line();
  188.             }
  189. #endif /* VEOF */
  190. #ifdef VKILL
  191.         } else if(cur_char == orig_termio.c_cc[VKILL] ){  /* ^U? */
  192.             clear_line(prompt);
  193. #endif /* VKILL */
  194. #ifdef VWERASE
  195.         } else if(cur_char == orig_termio.c_cc[VWERASE] ){  /* ^W? */
  196.             while((cur_pos > 0) &&
  197.                   (cur_line[cur_pos-1] == SPACE)) {
  198.                 cur_pos -= 1;
  199.                 putc(BACKSPACE, stderr);
  200.             }
  201.             while((cur_pos > 0) &&
  202.                   (cur_line[cur_pos-1] != SPACE)) {
  203.                 cur_pos -= 1;
  204.                 putc(BACKSPACE, stderr);
  205.             }
  206.             clear_eoline();
  207.             max_pos = cur_pos;
  208. #endif /* VWERASE */
  209. #ifdef VREPRINT
  210.         } else if(cur_char == orig_termio.c_cc[VREPRINT] ){  /* ^R? */
  211.             putc('\n',stderr); /* go to a fresh line */
  212.             redraw_line(prompt);
  213. #else
  214. #ifdef VRPRNT   /* on Ultrix VREPRINT is VRPRNT */
  215.         } else if(cur_char == orig_termio.c_cc[VRPRNT] ){  /* ^R? */
  216.             putc('\n',stderr); /* go to a fresh line */
  217.             redraw_line(prompt);
  218. #endif /* VRPRNT */
  219. #endif /* VREPRINT */
  220. #ifdef VSUSP
  221.         } else if(cur_char == orig_termio.c_cc[VSUSP]) {
  222.             reset_termio();
  223.             kill(0, SIGTSTP);
  224.  
  225.             /* process stops here */
  226.  
  227.             set_termio();
  228.             /* print the prompt */
  229.             redraw_line(prompt);
  230. #endif /* VSUSP */
  231.         } else {
  232.             /* do normal editing commands */
  233.             /* some of these are also done above */
  234.             int i;
  235.             switch(cur_char) {
  236.                 case EOF:
  237.                 reset_termio();
  238.                 return((char *)NULL);
  239.                 case 001: /* ^A */
  240.                 while(cur_pos > 0) {
  241.                     cur_pos -= 1;
  242.                     putc(BACKSPACE, stderr);
  243.                 }
  244.                 break;
  245.                 case 002: /* ^B */
  246.                 if(cur_pos > 0) {
  247.                     cur_pos -= 1;
  248.                     putc(BACKSPACE, stderr);
  249.                 }
  250.                 break;
  251.                 case 005: /* ^E */
  252.                 while(cur_pos < max_pos) {
  253.                     putc(cur_line[cur_pos], stderr);
  254.                     cur_pos += 1;
  255.                 }
  256.                 break;
  257.                 case 006: /* ^F */
  258.                 if(cur_pos < max_pos) {
  259.                     putc(cur_line[cur_pos], stderr);
  260.                     cur_pos += 1;
  261.                 }
  262.                 break;
  263.                 case 013: /* ^K */
  264.                 clear_eoline();
  265.                 max_pos = cur_pos;
  266.                 break;
  267.                 case 020: /* ^P */
  268.                 if(history != NULL) {
  269.                     if(cur_entry == NULL) {
  270.                         cur_entry = history;
  271.                         clear_line(prompt);
  272.                         copy_line(cur_entry->line);
  273.                     } else if(cur_entry->prev != NULL) {
  274.                         cur_entry = cur_entry->prev;
  275.                         clear_line(prompt);
  276.                         copy_line(cur_entry->line);
  277.                     }
  278.                 }
  279.                 break;
  280.                 case 016: /* ^N */
  281.                 if(cur_entry != NULL) {
  282.                     cur_entry = cur_entry->next;
  283.                     clear_line(prompt);
  284.                     if(cur_entry != NULL) 
  285.                         copy_line(cur_entry->line);
  286.                     else
  287.                         cur_pos = max_pos = 0;
  288.                 }
  289.                 break;
  290.                 case 014: /* ^L */
  291.                 case 022: /* ^R */
  292.                 putc('\n',stderr); /* go to a fresh line */
  293.                 redraw_line(prompt);
  294.                 break;
  295.                 case 0177: /* DEL */
  296.                 case 010: /* ^H */
  297.                 if(cur_pos > 0) {
  298.                     cur_pos -= 1;
  299.                     putc(BACKSPACE, stderr);
  300.                     for(i=cur_pos; i<max_pos; i++)
  301.                         cur_line[i] = cur_line[i+1];
  302.                     max_pos -= 1;
  303.                     fix_line();
  304.                 }
  305.                 break;
  306.                 case 004: /* ^D */
  307.                 if(max_pos == 0) {
  308.                     reset_termio();
  309.                     return((char *)NULL);
  310.                 }
  311.                 if(cur_pos < max_pos) {
  312.                     for(i=cur_pos; i<max_pos; i++)
  313.                         cur_line[i] = cur_line[i+1];
  314.                     max_pos -= 1;
  315.                     fix_line();
  316.                 }
  317.                 break;
  318.                 case 025:  /* ^U */
  319.                 clear_line(prompt);
  320.                 break;
  321.                 case 027:  /* ^W */
  322.                 while((cur_pos > 0) &&
  323.                       (cur_line[cur_pos-1] == SPACE)) {
  324.                     cur_pos -= 1;
  325.                     putc(BACKSPACE, stderr);
  326.                 }
  327.                 while((cur_pos > 0) &&
  328.                       (cur_line[cur_pos-1] != SPACE)) {
  329.                     cur_pos -= 1;
  330.                     putc(BACKSPACE, stderr);
  331.                 }
  332.                 clear_eoline();
  333.                 max_pos = cur_pos;
  334.                 break;
  335.                 break;
  336.                 case '\n': /* ^J */
  337.                 case '\r': /* ^M */
  338.                 cur_line[max_pos+1] = '\0';
  339.                 putc('\n', stderr);
  340.                 new_line = malloc(strlen(cur_line)+1);
  341.                 strcpy(new_line,cur_line);
  342.                 reset_termio();
  343.                 return(new_line);
  344.                 default:
  345.                 break;
  346.             }
  347.         }
  348.     }
  349. }
  350.  
  351. /* fix up the line from cur_pos to max_pos */
  352. /* do not need any terminal capabilities except backspace,
  353. /* and space overwrites a character */
  354. static void
  355. fix_line()
  356. {
  357.     int i;
  358.  
  359.     /* write tail of string */
  360.     for(i=cur_pos; i<max_pos; i++)
  361.         putc(cur_line[i], stderr);
  362.  
  363.     /* write a space at the end of the line in case we deleted one */
  364.     putc(SPACE, stderr);
  365.  
  366.     /* backup to original position */
  367.     for(i=max_pos+1; i>cur_pos; i--)
  368.         putc(BACKSPACE, stderr);
  369.  
  370. }
  371.  
  372. /* redraw the entire line, putting the cursor where it belongs */
  373. static void
  374. redraw_line(prompt)
  375. char *prompt;
  376. {
  377.     int i;
  378.  
  379.     fputs(prompt, stderr);
  380.     fputs(cur_line, stderr);
  381.  
  382.     /* put the cursor where it belongs */
  383.     for(i=max_pos; i>cur_pos; i--)
  384.         putc(BACKSPACE, stderr);
  385. }
  386.  
  387. /* clear cur_line and the screen line */
  388. static void
  389. clear_line(prompt)
  390. char *prompt;
  391. {
  392.     int i;
  393.     for(i=0; i<max_pos; i++)
  394.         cur_line[i] = '\0';
  395.  
  396.     for(i=cur_pos; i>0; i--)
  397.         putc(BACKSPACE, stderr);
  398.  
  399.     for(i=0; i<max_pos; i++)
  400.         putc(SPACE, stderr);
  401.  
  402.     putc('\r', stderr);
  403.     fputs(prompt, stderr);
  404.  
  405.     cur_pos = 0;
  406.     max_pos = 0;
  407. }
  408.  
  409. /* clear to end of line and the screen end of line */
  410. static void
  411. clear_eoline(prompt)
  412. char *prompt;
  413. {
  414.     int i;
  415.     for(i=cur_pos; i<max_pos; i++)
  416.         cur_line[i] = '\0';
  417.  
  418.     for(i=cur_pos; i<max_pos; i++)
  419.         putc(SPACE, stderr);
  420.     for(i=cur_pos; i<max_pos; i++)
  421.         putc(BACKSPACE, stderr);
  422. }
  423.  
  424. /* copy line to cur_line, draw it and set cur_pos and max_pos */
  425. static void
  426. copy_line(line)
  427. char *line;
  428. {
  429.     strcpy(cur_line, line);
  430.     fputs(cur_line, stderr);
  431.     cur_pos = max_pos = strlen(cur_line);
  432. }
  433.  
  434. /* add line to the history */
  435. void
  436. add_history(line)
  437. char *line;
  438. {
  439.     struct hist *entry;
  440.     entry = (struct hist *)malloc(sizeof(struct hist));
  441.     entry->line = malloc((unsigned int)strlen(line)+1);
  442.     strcpy(entry->line, line);
  443.  
  444.     entry->prev = history;
  445.     entry->next = NULL;
  446.     if(history != NULL) {
  447.         history->next = entry;
  448.     }
  449.     history = entry;
  450. }
  451.  
  452. #ifdef MSDOS
  453.  
  454. /* Convert Arrow keystrokes to Control characters: */
  455. static  char
  456. msdos_getch()
  457. {
  458.     char c = getch();
  459.  
  460.     if (c == 0) {
  461.     c = getch(); /* Get the extended code. */
  462.     switch (c) {
  463.         case 75: /* Left Arrow. */
  464.         c = 002;
  465.         break;
  466.         case 77: /* Right Arrow. */
  467.         c = 006;
  468.         break;
  469.         case 72: /* Up Arrow. */
  470.         c = 020;
  471.         break;
  472.         case 80: /* Down Arrow. */
  473.         c = 016;
  474.         break;
  475.         case 115: /* Ctl Left Arrow. */
  476.         case 71: /* Home */
  477.         c = 001;
  478.         break;
  479.         case 116: /* Ctl Right Arrow. */
  480.         case 79: /* End */
  481.         c = 005;
  482.         break;
  483.         case 83: /* Delete */
  484.         c = 004;
  485.         break;
  486.         default:
  487.         c = 0;
  488.         break;
  489.     }
  490.     }
  491.     else if (c == 033) { /* ESC */
  492.     c = 025;
  493.     }
  494.  
  495.  
  496.     return c;
  497. }
  498.  
  499. #endif /* MSDOS */
  500.  
  501. /* set termio so we can do our own input processing */
  502. static void
  503. set_termio()
  504. {
  505. #ifndef MSDOS
  506.     if(term_set == 0) {
  507. #ifdef TERMIOS
  508. #ifdef TCGETS
  509.         ioctl(0, TCGETS, &orig_termio);
  510. #else
  511.         tcgetattr(0, &orig_termio);
  512. #endif /* TCGETS */
  513. #else
  514.         ioctl(0, TCGETA, &orig_termio);
  515. #endif /* TERMIOS */
  516.         rl_termio = orig_termio;
  517.  
  518.         rl_termio.c_iflag &= ~(BRKINT|PARMRK|INPCK|IUCLC|IXON|IXOFF);
  519.         rl_termio.c_iflag |=  (IGNBRK|IGNPAR);
  520.  
  521.         rl_termio.c_oflag &= ~(ONOCR);
  522.  
  523.         rl_termio.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|NOFLSH);
  524.         rl_termio.c_lflag |=  (ISIG);
  525.  
  526.         rl_termio.c_cc[VMIN] = 1;
  527.         rl_termio.c_cc[VTIME] = 0;
  528.  
  529. #ifdef VSUSP
  530.         /* disable suspending process on ^Z */
  531.         rl_termio.c_cc[VSUSP] = 0;
  532. #endif /* VSUSP */
  533.  
  534. #ifdef TERMIOS
  535. #ifdef TCSETSW
  536.         ioctl(0, TCSETSW, &rl_termio);
  537. #else
  538.         tcsetattr(0, TCSADRAIN, &rl_termio);
  539. #endif /* TCSETSW */
  540. #else
  541.         ioctl(0, TCSETAW, &rl_termio);
  542. #endif /* TERMIOS */
  543.         term_set = 1;
  544.     }
  545. #endif /* MSDOS */
  546. }
  547.  
  548. static void
  549. reset_termio()
  550. {
  551. #ifndef MSDOS
  552.     if(term_set == 1) {
  553. #ifdef TERMIOS
  554. #ifdef TCSETSW
  555.         ioctl(0, TCSETSW, &orig_termio);
  556. #else
  557.         tcsetattr(0, TCSADRAIN, &orig_termio);
  558. #endif /* TCSETSW */
  559. #else
  560.         ioctl(0, TCSETAW, &orig_termio);
  561. #endif /* TERMIOS */
  562.         term_set = 0;
  563.     }
  564. #endif /* MSDOS */
  565. }
  566. #endif /* READLINE */
  567.